import asyncio
import os
import signal
from core import config
from core.interface.crawler_impl import AbstractCrawler
from core.platform.xhs.client import XiaoHongShuClient
from core.platform.xhs.login import XiaoHongShuLogin
from tools import utils
from typing import Dict, List, Optional, Tuple
from var import crawler_type_var

from playwright.async_api import (BrowserContext, BrowserType, Page,
                                  async_playwright)

from proxy.proxy_ip_pool import create_ip_pool
from proxy.types import IpInfoModel


class XhsCore(AbstractCrawler):
    """
    小红书爬虫实现类
    """

    def __init__(self) -> None:
        self.index_url = "https://www.xiaohongshu.com"
        self.user_agent = utils.get_user_agent()
        self.browser = None
        self.browser_closed = False
        self.letter_match = []

    def init_config(self, platform: str, login_type: str, crawler_type: str, start_page: int, keyword: str) -> None:
        self.platform = platform
        self.login_type = login_type
        self.crawler_type = crawler_type
        self.start_page = start_page
        self.keyword = keyword

    async def start(self) -> None:
        playwright_proxy_format, httpx_proxy_format = None, None
        if config.ENABLE_IP_PROXY:
            ip_proxy_pool = await create_ip_pool(config.IP_PROXY_POOL_COUNT, enable_validate_ip=True)
            ip_proxy_info: IpInfoModel = await ip_proxy_pool.get_proxy()
            playwright_proxy_format, httpx_proxy_format = self.format_proxy_info(
                ip_proxy_info)

        async with async_playwright() as playwright:
            chromium = playwright.chromium
            self.browser_context = await self.launch_browser(
                chromium,
                None,
                self.user_agent,
                headless=config.HEADLESS
            )

            await self.browser_context.add_init_script(path="libs/stealth.min.js")

            await self.browser_context.add_cookies([{
                'name': "webId",
                'value': "xxx123",  # any value
                'domain': ".xiaohongshu.com",
                'path': "/"
            }])

            self.context_page = await self.browser_context.new_page()
            await self.context_page.goto(self.index_url)

            self.xhs_client = await self.create_xhs_client(httpx_proxy_format)

            if not await self.xhs_client.pong():
                login_obj = XiaoHongShuLogin(
                    login_type=self.login_type,
                    login_phone="",  # input your phone number
                    browser_context=self.browser_context,
                    context_page=self.context_page,
                    cookie_str=config.COOKIES
                )
                await login_obj.begin()
                await self.xhs_client.update_cookies(browser_context=self.browser_context)

            crawler_type_var.set(self.crawler_type)

            if self.crawler_type == "letters":
                # Get letters
                await self.get_letters()
            else:
                pass

            utils.logger.info(
                "[XiaoHongShuCrawler.start] Xhs Crawler finished ...")

    async def get_letters(self) -> None:
        """Get letters"""
        utils.logger.info(
            "[XiaoHongShuCrawler.get_letters] Begin get letters ...")

        await self.get_letter(20)

        await self.setup_close_listener()

        try:
            # 等待浏览器关闭事件
            while not self.browser_closed:
                await asyncio.sleep(1)  # 短暂休眠以避免 CPU 占用过高

        finally:
            if not self.browser_closed:
                asyncio.create_task(self.close_browser())

    async def get_letter(self, num: int):
        """Get note letter"""
        utils.logger.info(
            f"[XiaoHongShuCrawler.get_comments] Begin get letter{num}")
        letter_res = await self.xhs_client.get_letters_by_num(
            num=20
        )

        message_list = letter_res.get("message_list", [])
        self.filter_post_item(message_list)

    def filter_post_item(self, post_item: list) -> dict:
        """根据特定关键词过滤评论数据，并根据匹配条件分类"""
        if not post_item:
            return {}

        # 定义关键词
        keywords = ["粉裙", "白鞋", "海边", "白兔", "黄帽"]

        filter_results = {
            "only_dress": [],      # 只含有“粉裙”
            "only_shoes": [],      # 只含有“白鞋”
            "only_seaside": [],    # 只含有“海边”
            "only_rabbit": [],     # 只含有“白兔”
            "only_hat": [],        # 只含有“黄帽”
            "two_keywords": [],    # 同时含有任意两个关键词
            "three_keywords": [],  # 同时含有任意三个关键词
            # 可以继续添加更多组合...
        }

        for item in post_item:
            comment_info = item.get('comment_info', '')
            found_keywords = set(
                filter(lambda kw: kw in comment_info, self.keywords))

            # 检查是否只含有一个关键词
            if len(found_keywords) == 1:
                keyword = found_keywords.pop()
                filter_key = f"only_{keyword}"
                filter_results[filter_key].append(item)

            # 检查是否同时含有任意两个关键词
            elif len(found_keywords) == 2:
                filter_results["two_keywords"].append(item)

            # 检查是否同时含有任意三个关键词
            elif len(found_keywords) == 3:
                filter_results["three_keywords"].append(item)

            # 如果有更多关键词组合，可以在这里继续添加逻辑

        print(filter_results)
        return filter_results

    async def get_reply_letter(self, note_id: str, target_comment_id: str, content: str):
        """Get note letter

            内容：item.comment_info.content
            target_comment_id: item.comment_info.id

            note_id: item.item_info.id
        """
        utils.logger.info(
            f"[XiaoHongShuCrawler.get_comments] Begin get letter{content}")
        reply_res = await self.xhs_client.get_reply(
            note_id,
            target_comment_id,
            content
        )

    async def setup_close_listener(self):
        # 监听浏览器关闭事件
        async def on_close():
            self.browser_closed = True
            print("Browser window closed. Exiting...")

        # 确保事件循环已经运行
        self.setup_signal_handler()

        # 为浏览器添加关闭事件监听器
        self.browser_context.on('close', on_close)

    async def setup_signal_handler(self):
        # 设置信号处理器，以便在接收到终止信号时关闭浏览器
        loop = asyncio.get_running_loop()
        loop.add_signal_handler(signal.SIGINT, self.signal_handler)
        loop.add_signal_handler(signal.SIGTERM, self.signal_handler)

    def signal_handler(self):
        # if not self.browser_closed:
        print("Received termination signal. Closing browser...")
        # 由于这是一个同步方法，我们不能直接在这里调用异步的 close_browser_context 方法
        # 我们需要安排一个任务在事件循环中运行 close_browser_context
        loop = asyncio.get_running_loop()
        # 注意这里使用 create_task 而不是 create_task
        loop.create_task(self.close_browser_context())

    async def close_browser(self):
        if self.browser:
            await self.browser.close()
            print("Browser closed.")

    async def close_browser_context(self):
        if self.browser_context:
            await self.browser_context.close()
            print("Browser context closed.")

    async def create_xhs_client(self, httpx_proxy: Optional[str]) -> XiaoHongShuClient:
        """Create xhs client"""
        utils.logger.info(
            "[XiaoHongShuCrawler.create_xhs_client] Begin create xiaohongshu API client ...")
        cookie_str, cookie_dict = utils.convert_cookies(await self.browser_context.cookies())
        xhs_client_obj = XiaoHongShuClient(
            proxies=httpx_proxy,
            headers={
                "User-Agent": self.user_agent,
                "Cookie": cookie_str,
                "Origin": "https://www.xiaohongshu.com",
                "Referer": "https://www.xiaohongshu.com",
                "Content-Type": "application/json;charset=UTF-8"
            },
            playwright_page=self.context_page,
            cookie_dict=cookie_dict,
        )
        return xhs_client_obj

    async def launch_browser(
        self,
        chromium: BrowserType,
        playwright_proxy: Optional[Dict],
        user_agent: Optional[str],
        headless: bool = True
    ) -> BrowserContext:
        utils.logger.info(
            "[XiaoHongShuCrawler.launch_browser] Begin create browser context ...")
        if config.SAVE_LOGIN_STATE:
            # feat issue #14
            # we will save login state to avoid login every time
            user_data_dir = os.path.join(os.getcwd(), "browser_data",
                                         config.USER_DATA_DIR % self.platform)  # type: ignore
            browser_context = await chromium.launch_persistent_context(
                user_data_dir=user_data_dir,
                accept_downloads=True,
                headless=headless,
                proxy=playwright_proxy,  # type: ignore
                viewport={"width": 1920, "height": 1080},
                user_agent=user_agent
            )
            return browser_context
        else:
            # type: ignore
            browser = await chromium.launch(headless=headless, proxy=playwright_proxy)
            browser_context = await browser.new_context(
                viewport={"width": 1920, "height": 1080},
                user_agent=user_agent
            )
            return browser_context

    async def close(self):
        await self.browser_context.close()
        utils.logger.info(
            "[XiaoHongShuCrawler.close] Browser context closed ...")
